#include "zlib_tool.h"

#include <stdio.h>
#include <string.h>
#include <assert.h>

ZlibTool::ZlibTool()
    : m_strm(nullptr)
{
}

ZlibTool::~ZlibTool()
{
    delete m_strm;
}

void ZlibTool::reset()
{
    m_compressStream.clear();
    if (m_strm) {
        (void)inflateEnd(m_strm);
    }
    delete m_strm;
    m_strm = nullptr;
}

std::vector<uint8_t> ZlibTool::compress(const char *data, int len)
{
    std::vector<uint8_t> compressData;
    uLong destLen = compressBound(len);
    Bytef *dest = new Bytef[destLen];
    if (Z_OK == compress2(dest, &destLen, (Bytef *)data, len, Z_DEFAULT_COMPRESSION)) {
        compressData.insert(compressData.begin(), dest, dest + destLen);
    }

    delete[] dest;
    return compressData;
}

std::vector<uint8_t> ZlibTool::decompress(const char *data, int len)
{
    std::vector<uint8_t> desData;
    if (len <= 0) {
        return desData;
    }

    m_compressStream.write(data, len);

    int ret;
    if (!m_strm) {
        m_strm = new z_stream;
        /* allocate inflate state */
        m_strm->zalloc = Z_NULL;
        m_strm->zfree = Z_NULL;
        m_strm->opaque = Z_NULL;
        m_strm->avail_in = 0;
        m_strm->next_in = Z_NULL;
        ret = inflateInit(m_strm);
        if (ret != Z_OK) {
            delete m_strm;
            m_strm = nullptr;
            return desData;
        }
    }

    unsigned char out[CHUNK];
    unsigned char in[CHUNK];

    /* decompress until deflate stream ends or end of file */
    do {
        m_strm->avail_in = m_compressStream.readsome((char *)in, CHUNK);
        if (m_strm->avail_in == 0)
            break;
        m_strm->next_in = in;
        /* run inflate() on input until output buffer not full */
        do {
            m_strm->avail_out = CHUNK;
            m_strm->next_out = out;
            ret = inflate(m_strm, Z_NO_FLUSH);
            assert(ret != Z_STREAM_ERROR); /* state not clobbered */
            switch (ret) {
            case Z_NEED_DICT:
                ret = Z_DATA_ERROR; /* and fall through */
            case Z_DATA_ERROR:
            case Z_MEM_ERROR:
                goto end;
            }
            desData.insert(desData.end(), out, out + CHUNK - m_strm->avail_out);
        } while (m_strm->avail_out == 0);
        /* done when inflate() says it's done */
    } while (ret != Z_STREAM_END);

end:
    return desData;
}
